package calao.util;

import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.io.FileOutputStream;
import java.io.PrintWriter;

public class FileHandler
extends LogHandler
implements Closeable
{
	
	public static final long DEFAULT_FILE_MAX_SIZE = 1048576L;
	
	public static final int DEFAULT_MAX_FILES = 10;
	
	class CycleException extends RuntimeException { }

	class LogStream extends FileOutputStream {
		final File file;
		long fileSize;
		LogStream(File file) throws IOException {
			super(file, true);
			this.file = file;
			fileSize = file.length();
		}
		@Override
		public void write(int b) throws IOException {
			super.write(b);
			fileSize++;
		}
		@Override
		public void write(byte[] b) throws IOException {
			super.write(b);
			fileSize += b.length;
		}
		@Override
		public void write(byte[] b, int off, int len) throws IOException {
			super.write(b, off, len);
			fileSize += len;
		}
	}
	
	class LogWriter extends PrintWriter {
		final LogStream stream;
		LogWriter(LogStream stream) throws IOException {
			super(stream);
			this.stream = stream;
		}
	}
	
	final String filePattern;
	final long fileMaxSize;
	final int maxFiles;
	
	LogWriter writer;
	
	public FileHandler(String filePattern)
	throws IOException
	{
		this(filePattern, DEFAULT_FILE_MAX_SIZE, DEFAULT_MAX_FILES);
	}
	
	public FileHandler(String filePattern, long fileMaxSize, int maxFiles)
	throws IOException
	{
		this.filePattern = filePattern;
		this.fileMaxSize = fileMaxSize;
		this.maxFiles = maxFiles;
		writer = new LogWriter(new LogStream(getFile(0)));
		if (maxFiles<1) throw new IllegalArgumentException();
	}
	
	private File getFile(int index)
	{
		StringBuffer buffer = new StringBuffer();
		int state = 0;
		for (int i=0; i<filePattern.length(); i++) {
			char c = filePattern.charAt(i);
			if (state==1) {
				switch (c) {
				case 'i':
					buffer.append(index);
					break;
				}
				state = 0;
			}
			else if (c=='%') {
				state = 1;
				continue;
			}
			else buffer.append(c);
		}
		return new File(buffer.toString());
	}
	
	public void handle(LogRecord record) 
	{
		try {
			if (writer.stream.fileSize>fileMaxSize) {
				writer.close();
				if (!cycleFiles()) throw new CycleException();
				writer = new LogWriter(new LogStream(getFile(0)));
			}
			printTo(record, writer);
		}
		catch (IOException e) {
			throw new RuntimeException(e);
		}
	}
		
	private boolean cycleFiles()
	{
		File[] files = new File[maxFiles];
		for (int i=0; i<maxFiles; i++) {
			files[i] = getFile(i);
		}
		int lastIndex = maxFiles - 1;
		File last = files[lastIndex];
		if (last.exists() && !last.delete()) return false;
		for (int i=lastIndex-1; i>=0; i--) {
			if (files[i].exists() && !files[i].renameTo(files[i+1])) return false;
		}
		return true;
	}

	public void close() 
	{
		writer.close();
	}

}
